/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.ove.openmath.mathematica;

/**
 *
 * @author Mr.Sam
 */
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.io.*;
import fr.ove.openmath.mathematica.*;
import fr.ove.openmath.mathematica.events.*;
import fr.ove.utils.*;

/**
*
*/
public class OMWorkSheet extends WorkSheet implements PanelJomesListener, Savable, Runnable {
    /**
    * The current element in the work sheet
    */
    private PanelJomes current;
    
    /**
    * The list of elements in the work sheet
    */
    private Vector listElements = new Vector();
    
    /**
    * The work book for the frame contaninng the instance.
    */
    private WorkBook workbook;
    
    /**
    * Indicates if the instance need to be saved.
    */
    private boolean needed = true;
    
    /**
    * The connection factory
    */
    private Connection connection;
    
    /**
    * The result reader.
    */
    private ResponseReader reader = new ResponseReader();
    
    /**
    * The claculation queue
    */
    private fr.ove.utils.Queue calculations = new fr.ove.utils.Queue();
    
    /**
    * The constructor.
    */
    public OMWorkSheet(WorkBook workbook, ConnectionFactory factory) {
        super();
        this.workbook = workbook;
        
        connection = factory.createConnection();
        try {
            connection.open();
        }
        catch (IOException ioe) {
            String errorMsg = "failed to open a connection to [" + 
                              connection.getHostName() + ", " + connection.getPortNumber() +
                              "]";
            JOptionPane jop = new JOptionPane(errorMsg, JOptionPane.ERROR_MESSAGE);
            JDialog dialog = jop.createDialog(workbook, "Connection Failed");
            dialog.show();
        }
        
        addNewElement();
        
        Thread t = new Thread(this);
        t.start();
    }
    
    /**
    * Adds a new element to the work sheet.
    */
    public void addNewElement() {
        if (current != null)
            current.setSelected(false);
            
        current = new PanelJomes();
        current.addPanelJomesListener(this);
        listElements.addElement(current);
        add(current);
        needed = true;
    }
    
    /**
    * Inserts a new element.
    */
    public void insertNewElement() {
        int indexCurrent = -1;
        
        if (current != null) {
            current.setSelected(false);
            indexCurrent = listElements.indexOf(current);
        }
        
        current = new PanelJomes();
        current.addPanelJomesListener(this);
        if (indexCurrent != -1) {
            listElements.insertElementAt(current, indexCurrent);
            add(current, indexCurrent);
        }
        else {
            listElements.addElement(current);
            add(current);
        }
        
        needed = true;
        
        workbook.fireStateChanged();
        validate();
        repaint();
    }
    
	/**
	* Removes the specified component from the instance.
	* @param comp the component to remove
	*/
	public void remove(Component comp) {
	    super.remove(comp);
	    listElements.removeElement(comp);
        needed = true;
	}
	
	/**
	* Removes the component at the specified index from the instance.
	* @param index the index of the component to remove
	*/
	public void remove(int index) {
	    super.remove(index);
	    listElements.removeElementAt(index);
        needed = true;
	}
	
	/**
	* Removes all the components from the instance
	*/
	public void removeAll() {
	    super.removeAll();
	    listElements.setSize(0);
        needed = true;
	}
	
	/**
	* Remove the current element in the worksheet
	*/
	public void removeCurrent() {
	    // keep track of current's index in the list.
	    int index = listElements.indexOf(current);
	    remove(current);
	    int nbElements = listElements.size();
	    if (nbElements > 0) {
	        index = (index >= nbElements) ? nbElements - 1 : index;
	        current = (PanelJomes) listElements.elementAt(index);
	        current.setSelected(true);
	    }
	    else
	        addNewElement();
	        
        workbook.fireStateChanged();
        validate();
        repaint();
	}
	
    /**
    * Initializes the work sheet, i.e. removes all the existing elements and
    * and put an empty one.
    */
    public void initialize() {
        super.removeAll();
        listElements.setSize(0);
        addNewElement();
        workbook.fireStateChanged();
        validate();
        repaint();
    }
    
    /**
    * Returns the current element in the worksheet
    */
    public PanelJomes getCurrentElement() {
        return current;
    }
    
    /**
    * Returns the preferred size.
    */
    public Dimension getPreferredSize() {
        return new Dimension(450, 300);
    }
    
    /**
    * Returns the connection.
    */
    public Connection getConnection() {
        return connection;
    }
    
    /**
    * Runs the current request
    */
    public void runCurrentRequest() {
        //run(current);
        calculations.enqueue(current);
    }
    
    /**
    * Runs the work sheet.
    */
    public void runWorkSheet() {
        /*
        PanelJomes pj;
        for (Enumeration e = listElements.elements(); e.hasMoreElements(); ) {
            pj = (PanelJomes) e.nextElement();
            if (!run(pj))
                return;
        }
        */
        
        Thread t = new Thread(
            new Runnable() {
                public void run() {
                    PanelJomes pj;
                    for (Enumeration e = listElements.elements(); e.hasMoreElements(); ) {
                        pj = (PanelJomes) e.nextElement();
                        if (!OMWorkSheet.this.run(pj))
                            return;
                            
                        try {
                            Thread.sleep(100);
                        }
                        catch (Exception exc) {
                        }
                    }
                }
            }
        );
        t.start();
                    
    }
    
    /**
    * Runs the specified element.
    * @param element the element to run.
    * @return <CODE>true</CODE> if run succeeds. <CODE>false</CODE> otherwise.
    */
    private boolean run(PanelJomes element) {
        String request = element.getOpenMathRequest();
        if (connection.isOpened() && !request.equals("")) {
            try {
        	    connection.getOutputStream().write(request.getBytes());
        	    String response = reader.read(connection.getInputStream());
        	    if (response != null) {
        	        element.setOpenMathResult(response);
        	        return true;
        	    }
            	
        	    return false;
            }
            catch (IOException ioe) {
                String errorMsg = "Failed to execute calculation : I/O Problems encountered";
                JOptionPane jop = new JOptionPane(errorMsg, JOptionPane.ERROR_MESSAGE);
                JDialog dialog = jop.createDialog(workbook, "Caculation Failed : I/O Problems");
                dialog.show();
                return false;
            }
        }
        else
            return false;
    }
    
    
    // ### PanelJomesListener interface implementation
    
    /**
    * Consumes (i.e. treats) the event received.
    * @param panelJomesEvent the event to consume.
    */
    public void consumePanelJomesEvent(PanelJomesEvent panelJomesEvent) {
        int action = panelJomesEvent.getAction();
        switch (action) {
            case PanelJomesEvent.UPDATE_REQUEST :
            case PanelJomesEvent.UPDATE :
                needed = true;
                workbook.fireStateChanged();
                break;
            case PanelJomesEvent.UPDATE_RESULT :
                needed = true;
                workbook.fireStateChanged();
                break;
            case PanelJomesEvent.UPDATE_SELECTION :
                current = (PanelJomes) panelJomesEvent.getSource();
            
                for (Enumeration e = listElements.elements(); e.hasMoreElements(); )
                    ((PanelJomes) e.nextElement()).setSelected(false);
                    
                current.setSelected(true);
                workbook.fireStateChanged();
                repaint();
        }
    }
    
    // ### Savable interface implementation
        
    /**
    * Saves the instance.
    * @returns the result of the save action. Should be <CODE>null</CODE> if nothing
    * is expected after the save operation. The saved object otherwise.
    * The instance is saved as the concatenation of all the OpenMath object request contained.
    */
    public Object save() {
        StringBuffer buffer = new StringBuffer();
        for (Enumeration e = listElements.elements(); e.hasMoreElements(); )
            buffer.append(((PanelJomes) e.nextElement()).getOpenMathRequest() + "\n");
            
        setSaveNeeded(false);
                
        return buffer.toString();
    }
    
    /**
    * Saves the instance into the specified file.
    * @returns the result of the save action. Should be <CODE>null</CODE> if nothing
    * is expected after the save operation. The saved object otherwise.
    */
    public Object save(File file) {
        return null;
    }
        
    /**
    * Saves the instance with the specified name.
    * @param name the specified name.
    * @returns the result of the save action. Should be <CODE>null</CODE> if nothing
    * is expected after the save operation. The saved object otherwise.
    */
    public Object saveAs(String name) {
        return null;
    }
        
    /**
    * Checks if the instance need to be saved.
    * @return <CODE>true</CODE> if the instance need to be saved. <CODE>false</CODE> otherwise.
    */
    public boolean isSaveNeeded() {
        return needed;
    }
        
    /**
    * Sets if the instance need to be saved.
    * @param needed <CODE>true</CODE> if the instance need to be saved. <CODE>false</CODE> otherwise.
    */
    public void setSaveNeeded(boolean needed) {
        this.needed = needed;
    }
    
    // ###  Runnable interface implementation.
    
    /**
    * run the thread
    */
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            }
            catch (Exception e) {
            }
            
            while (!calculations.isEmpty()) {
                run((PanelJomes) calculations.dequeue());
            }
        }
    }
}